﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace Editor
{
    public partial class MainForm : Form, Context.SharedData
    {
        Game.Set<Context.LockedAxisMode> mLockedAxisModeSet = new Game.Set<Context.LockedAxisMode>();

        public Game.Set<Context.LockedAxisMode> LockedAxisModeSet
        {
            get
            {
                //update it
                mLockedAxisModeSet.Apply(Context.LockedAxisMode.X, !btnX.Checked);
                mLockedAxisModeSet.Apply(Context.LockedAxisMode.Y, !btnY.Checked);
                mLockedAxisModeSet.Apply(Context.LockedAxisMode.Z, !btnZ.Checked);

                return mLockedAxisModeSet;
            }
        }

        public Context.TranformAxisMode TranformMode
        {
            get
            {
                return (btnMove.Checked) ? Context.TranformAxisMode.Move : (btnRotate.Checked) ? Context.TranformAxisMode.Rotate : Context.TranformAxisMode.None; 
            }
        }

        public IDK.Camera Camera
        {
            get;
            set;
        }

        public IDK.Engine Engine
        {
            get;
            set;
        }

        public IDK.SceneNode SceneObjects
        {
            get;
            set;
        }

        public IDK.SceneNode SystemObjects
        {
            get;
            set;
        }

        public IDK.SceneNode AxisNode
        {
            get;
            set;
        }

        public IDK.SceneNode GridNode
        {
            get;
            set;
        }

        public Game.DataBase.Manager DataBaseManager
        {
            get;
            set;
        }

        private UInt32 mLevelId = 8;

        public UInt32 LevelId
        {
            get { return mLevelId; }
            set { mLevelId = value; }
        }

        public IDK.SceneNode AddMapObject(Game.DataBase.Entities.MapObject item)
        {
            return mGameState.AddMapObject(item, null);
        }

        private Game.StateManager mStateManager = null;

        private Game.GameSavingState mSavingState = null;
        private EditorLoadingState mLoadingState = null;
        private Game.GameState mGameState = null;
        private Game.GameUnloadingState mUnloadingState = null;

        private Context.Base CurrentContext = null;

        private String mRootPath;

        private Dictionary<Keys, bool> mKeyPress = new Dictionary<Keys, bool>();
        private Dictionary<Keys, Game.Events.UserInput.Key> mKeyToIDK = new Dictionary<Keys, Game.Events.UserInput.Key>();

        private Game.CommonAction.ActionManager mActionManager = new Game.CommonAction.ActionManager();

        public MainForm()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);

            InitializeComponent();
        }

        public virtual void RemoveNode(IDK.SceneNode node)
        {
            //reset selected
            objectInspector.SelectedObject = null;

            //get first
            nodesList.Nodes.Remove(nodesList.Nodes.Find(node.GetName(), true)[0]);
        }

        public virtual void SelectNode(IDK.SceneNode node, bool lookAt)
        {
            if (node == null)
            {
                objectInspector.SelectedObject = null;
            }
            else
            {
                IDK.SceneNode.Transformation trans = node.GetTransformation();

                if (trans != null)
                {

                    var newSelect = new EditorSceneGameObject(node, mGameState.GameScene);
                    
                    //find gameobject by node name
                    var manager = mGameState.GameScene.GetManager<Game.GameObjectManager>();
                    if (manager.HasGameObject(node.GetName()))
                    {
                        newSelect.GameObjectInfo = manager.GameObjectsType[node.GetName()];
                    }

                    objectInspector.SelectedObject = newSelect;

                    if (lookAt)
                    {
                        if (trans != null)
                        {
                            mLoadingState.CameraObserver.SetPosition(trans.GetWorldPosition());
                        }
                    }
                }
                else
                {
                    objectInspector.SelectedObject = null;
                }
            }
        }

        private void Message(String text)
        {

            logBox.Items.Add(text);
            logBox.SetSelected(logBox.Items.Count - 1, true);
            logBox.SetSelected(logBox.Items.Count - 1, false);
            /*
            logBox.SelectedIndex = logBox.Items.Count-1;
            logBox.SelectedIndex = -1;*/
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            mKeyToIDK.Add(Keys.Escape, Game.Events.UserInput.Key.Esc);
            mKeyToIDK.Add(Keys.Space, Game.Events.UserInput.Key.Space);
            mKeyToIDK.Add(Keys.ShiftKey, Game.Events.UserInput.Key.Shift);

            mKeyToIDK.Add(Keys.E, Game.Events.UserInput.Key.E);

            mKeyToIDK.Add(Keys.S, Game.Events.UserInput.Key.S);
            mKeyToIDK.Add(Keys.W, Game.Events.UserInput.Key.W);
            mKeyToIDK.Add(Keys.A, Game.Events.UserInput.Key.A);
            mKeyToIDK.Add(Keys.D, Game.Events.UserInput.Key.D);

            Game.Log.Message += new Game.Log.MessageHandler(Message);

            IDK.Viewport viewport = Engine.GetRenderer().CreateViewport(1024, 768, viewer.Handle);
            //IDK.Viewport viewport = Engine.GetRenderer().CreateViewport((uint)viewer.Width, (uint)viewer.Height, viewer.Handle);
            Engine.GetRenderer().SetViewport(viewport);

            Engine.Start();

            //Set default path
            mRootPath = System.IO.Path.GetFullPath(System.IO.Directory.GetCurrentDirectory() + @"\..\Resources\");
            Engine.GetCore().SetDefaultResourcePath(mRootPath);

            DataBaseManager = new Game.DataBase.Manager(mRootPath + @"\db\global.s3db");

            mStateManager = new Game.StateManager();

            var uiProgressBar = new UI.ProgressBar(progressBar, progressLabel);

            mSavingState = new Game.GameSavingState(DataBaseManager);
            mSavingState.ProgressBar = uiProgressBar;

            mLoadingState = new EditorLoadingState(DataBaseManager);
            mLoadingState.ProgressBar = uiProgressBar;

            mGameState = new Game.GameState(DataBaseManager);
            mUnloadingState = new Game.GameUnloadingState(DataBaseManager);
            mUnloadingState.ProgressBar = uiProgressBar;

            mStateManager.Clear();
            mStateManager.Add(mLoadingState, mGameState);
            mStateManager.Add(mGameState, mUnloadingState);
            mStateManager.Add(mUnloadingState, null);


            Game.Log.Hint("PrepereEditor");

            PrepereEditor();

            Game.Log.Hint("LoadScene");

            LoadScene(LevelId);

            Game.Log.Hint("Editor create");
        }

        private void PrepereEditor()
        {
            //load all info db for editor know all
            Game.Log.Hint("DB CacheAll");

            DataBaseManager.CacheAll();

            Game.Log.Hint("load icons");
            //cache all models
            foreach (KeyValuePair<UInt32, Game.DataBase.Entities.Model> it in DataBaseManager.GetModels())
            {
                ///
                Game.DataBase.Entities.Mesh dbMesh = DataBaseManager.GetMeshByID(it.Value.meshId);

                String pathIco = mRootPath + @"\Meshes\" + Path.GetDirectoryName(dbMesh.path) + @"\ico.jpg";
                int imageIndex = 0;

                if (File.Exists(pathIco))
                {
                    imageList1.Images.Add(Image.FromFile(pathIco));

                    imageIndex = imageList1.Images.Count - 1;
                
                    ListViewItem item = new ListViewItem(it.Value.name, imageIndex);
                    item.Tag = it.Value;
                    objectList.Items.Add(item);
                }
            }
        }

        private void ResetEditor()
        {
            //reset select object
            objectInspector.SelectedObject = null;

            //unlink
            CurrentContext = new Context.UnSelected(this);
        }

        private void UnLoadScene()
        {
            ResetEditor();

            // when waiting to be unloading
            mGameState.Quit = true;

            Game.Events.ProcessUpdate evntUpdate = new Game.Events.ProcessUpdate()
            {
                ElapsedTime = 1.0f / 60.0f
            };

            // update and wait unloading
            while (UpdateEngine(evntUpdate))
            {
                //wait == false
            }

            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        private void LoadScene(UInt32 levelId)
        {
            nodesList.Nodes.Clear();

            //TODO: FIX TEMP
            IDK.Viewport viewport = Engine.GetRenderer().GetViewport();
            viewport.Width = 1024;
            viewport.Height = 768;

            EditorScene scene = new EditorScene(Engine);
            scene.ActionManager = mActionManager;
            mLoadingState.GameScene = scene;
            mLoadingState.LevelId = levelId;

            mGameState.GameScene = scene;
            mUnloadingState.GameScene = scene;

            mStateManager.SetState(mLoadingState);

            // this must be, because we will activate loading state for to get maked camera  
            Camera = mLoadingState.GameScene.Camera;

            SceneObjects = Engine.GetCore().GetScene().GetNode("Root");


            //Create grid
            {
                GridNode = Engine.GetCore().GetScene().AddNode("Grid", null);
                GridNode.MakeTransformation(IDK.Vector3.Zero,
                         IDK.Quaternion.Identity,
                         IDK.AABBox.Infinity,
                         IDK.Vector3.One);

                IDK.ShaderProgram program = Engine.GetCore().GetShaderProgram("DebugShading\\Grid");
                program.AddParameter(IDK.ShaderProgram.Parameter.Type.VertexAttribute, IDK.ShaderProgram.Parameter.Declaration.VertexPosition, "position");

                program.AddParameter(IDK.ShaderProgram.Parameter.Type.ConstantParameter, IDK.ShaderProgram.Parameter.Declaration.MatrixProjection, "gProj");
                program.AddParameter(IDK.ShaderProgram.Parameter.Type.ConstantParameter, IDK.ShaderProgram.Parameter.Declaration.MatrixView, "gView");
                program.AddParameter(IDK.ShaderProgram.Parameter.Type.ConstantParameter, IDK.ShaderProgram.Parameter.Declaration.MatrixWorld, "gWorld");

                GridNode.AddRenderable(new IDK.Grid2D(program));
                GridNode.Visible = false;
            }

            //Create Axis
            /*
            
            AxisNode = Engine.GetCore().GetScene().AddNode("Axis", SceneObjects);
            AxisNode.MakeTransformation(IDK.Vector3.Zero,
                     IDK.Quaternion.Identity,
                     new IDK.AABBox(new IDK.Vector3(-1, -1, -1), new IDK.Vector3(1, 1, 1)),
                     IDK.Vector3.One);

            IDK.SceneNode AxisX = Engine.GetCore().GetScene().AddNode("AxisX", AxisNode);

            AxisX.MakeTransformation(IDK.Vector3.Zero,
                IDK.Quaternion.Identity,
                new IDK.AABBox(new IDK.Vector3(-1, -1, -1), new IDK.Vector3(1, 1, 1)),
                IDK.Vector3.One);

            AxisNode.AddRenderable(new IDK.CoordinateAxes());*/

            CurrentContext = new Context.UnSelected(this);

            Game.Events.ProcessUpdate evntUpdate = new Game.Events.ProcessUpdate()
            {
                ElapsedTime = 1.0f / 60.0f
            };
            UpdateEngine(evntUpdate);

            //Load Nodes Info
            ActBuildNodeList(nodesList.Nodes, mGameState.GameScene.Core.GetScene().GetRootNodes());

            UpdateScreenSize();
        }

        private void ActBuildNodeList(TreeNodeCollection nodes, IDK.SceneNodesList list)
        {
            //Cleanup old data
            nodes.Clear();

            //Iteration for each node
            foreach (IDK.SceneNodesList.NodeItem it in list)
            {
                IDK.SceneNode sceneNode = it.Current();
                TreeNode node = nodes.Add(sceneNode.GetName(), sceneNode.GetName());

                node.Tag = sceneNode;

                ActBuildNodeList(node.Nodes, it.GetChildren());
            }
        }

        public bool UpdateEngine(Game.Events.ProcessUpdate evnt)
        {
            Engine.Update(evnt.ElapsedTime);

            //
            CurrentContext.Update(evnt.ElapsedTime);

            if (mStateManager.Update(evnt))
            {
                return true;
            }

            return false;
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            UnLoadScene();
            LoadScene(LevelId);
        }

        private void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            //
            if (mKeyPress.ContainsKey(e.KeyCode) == false)
            {
                mKeyPress.Add(e.KeyCode, false);
            }

            if (mKeyToIDK.ContainsKey(e.KeyCode) && mKeyPress[e.KeyCode] == false)
            {
                //Game.Log.Hint(e.KeyData.ToString());
                mKeyPress[e.KeyCode] = true;
                mActionManager.InputSubscriber.OnKeyDown(new Game.Events.UserInput(mKeyToIDK[e.KeyCode]));
            }
        }

        private void MainForm_KeyUp(object sender, KeyEventArgs e)
        {
            if (mKeyToIDK.ContainsKey(e.KeyCode))
            {
                mKeyPress[e.KeyCode] = false;
                mActionManager.InputSubscriber.OnKeyUp(new Game.Events.UserInput(mKeyToIDK[e.KeyCode]));
            }
        }

        private Context.MouseEvent MouseFrom(MouseEventArgs e)
        {
            switch (e.Button)
            {
                case System.Windows.Forms.MouseButtons.None:
                    return new Context.MouseEvent(Context.MouseEvent.ButtonCode.None, e.X, e.Y);
                case System.Windows.Forms.MouseButtons.Left:
                    return new Context.MouseEvent(Context.MouseEvent.ButtonCode.Left, e.X, e.Y);
                case System.Windows.Forms.MouseButtons.Right:
                    return new Context.MouseEvent(Context.MouseEvent.ButtonCode.Right, e.X, e.Y);
                case System.Windows.Forms.MouseButtons.Middle:
                    return new Context.MouseEvent(Context.MouseEvent.ButtonCode.Middle, e.X, e.Y);
            }

            throw new NullReferenceException("not found convert type");
        }

        private void viewer_MouseUp(object sender, MouseEventArgs e)
        {
            CurrentContext = CurrentContext.MouseUp(MouseFrom(e));
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            if (mStateManager.Current == mGameState)
            {
                mSavingState.Activate(mGameState.GameScene);
                mSavingState.Deactivate();
            }
        }


        private void viewer_MouseEnter(object sender, EventArgs e)
        {
            //check new object
            ListView.SelectedListViewItemCollection items = objectList.SelectedItems;
            if (items.Count > 0)
            {
                CurrentContext = new Context.MakeMapObject(CurrentContext.Data, items[0].Tag as Game.DataBase.Entities.Model);
                //reset
                objectList.SelectedIndices.Clear();
            }

            viewBox.Focus();
        }

        private void nodesList_AfterSelect(object sender, TreeViewEventArgs e)
        {
            CurrentContext = new Context.Selected(this, e.Node.Tag as IDK.SceneNode, true);
        }

        private void btnAddNewModel_Click(object sender, EventArgs e)
        {
            // select dlg
            DataBaseManager.GenMesh("test", new IDK.Vector3(0), new IDK.Vector3(0));
        }

        private void viewer_MouseMove(object sender, MouseEventArgs e)
        {
            CurrentContext = CurrentContext.MouseMove(MouseFrom(e));
        }

        private void UpdateScreenSize()
        {
            if (Engine != null)
            {
                IDK.Viewport viewport = Engine.GetRenderer().GetViewport();
                viewport.Width = (uint)viewer.Width;
                viewport.Height = (uint)viewer.Height;
            }
        }

        private void MainForm_SizeChanged(object sender, EventArgs e)
        {
            UpdateScreenSize();
        }

        private void viewer_MouseDown(object sender, MouseEventArgs e)
        {
            CurrentContext = CurrentContext.MouseDown(MouseFrom(e));
        }

        private void btnMove_Click(object sender, EventArgs e)
        {
            ToolStripButton btn = (sender as ToolStripButton);
            btnMove.Checked = false;
            btnRotate.Checked = false;
            btnTouch.Checked = false;

            if (btn == btnRotate)
            {
                btnRotate.Checked = true;
            }
            else if (btn == btnMove)
            {
                btnMove.Checked = true;
            }
            else if (btn == btnTouch)
            {
                btnTouch.Checked = true;
            }
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            if(CurrentContext is Context.Selected)
            {
                var c = CurrentContext as Context.Selected;
                CurrentContext = new Context.RemoveMapObject(c.Data, c.SceneNode);
            }
        }

        private void objectInspector_Click(object sender, EventArgs e)
        {

        }

        private void btnSimulate_Click(object sender, EventArgs e)
        {
            //
            mGameState.GameScene.Pause = !mGameState.GameScene.Pause;
            
            //Invalidate - process re-calculate game logic by changed data
            if (!mGameState.GameScene.Pause)
            {
                foreach (var it in mGameState.GameScene.Managers)
                {
                    it.Invalidate();
                }
            }

            //
            mLoadingState.CameraObserver.Enable = mGameState.GameScene.Pause;

            btnSimulate.Text = (mGameState.GameScene.Pause) ? "Play" : "Pause";
        }

        private void btnGrid_Click(object sender, EventArgs e)
        {
            GridNode.Visible = !GridNode.Visible;
        }

        private void btnSelectObject_Click(object sender, EventArgs e)
        {
            Game.Log.Warn("Render");
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            Game.Log.Warn("Editor close");
            Game.Log.Message -= new Game.Log.MessageHandler(Message);
        }
    }
}
